/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ufps.cuatro.util.dnd;

import com.ufps.cuatro.controlador.render.UtilRender;
import com.ufps.cuatro.modelo.representacion.componente.Componente;
import com.ufps.cuatro.modelo.representacion.componente.ComponenteDistribuidor;
import com.ufps.cuatro.modelo.representacion.PlantillaCombo;
import com.ufps.cuatro.modelo.representacion.PlantillaComponente;
import com.ufps.cuatro.util.Utileria;
import com.ufps.cuatro.util.generadores.GeneradorComponentes;
import com.ufps.cuatro.util.render.MyListRendererJLabel;
import com.ufps.cuatro.util.render.MyTableCellRendererDistribucionComponentes;
import java.awt.GridBagConstraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DropMode;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

/**
 *
 * @author Carlos A. Torres C. 
 */
public class ControlarTablaDistribucion {
    
    /**
     * Indica Distribución Horizontal.
     */
    public final static int UPDATE_SIZE_X = 0;
    /**
     * Indica Distribución Vertical.
     */
    public final static int UPDATE_SIZE_Y = 1;
    /**
     * Indica desplazamiento.
     */
    public final static int UPDATE_MOVE_XY = 2;
    /**
     * Multiplo por el cual se Ajustarán los Columnas.
     * El valor intrno inicia de Ajuste es de 20.
     */
    public static int SIZE_COLUMN;
    
    private JTable objTable;        
    private JList objList;
    private ComponenteDistribuidor objComponenteDistribuidorParent;
    
    static{
        try{
            String strTemp = Utileria.leerRecursoTexto("Utileria.constante.ajuste.columna");
            if(strTemp.isEmpty()){
                SIZE_COLUMN = 0;
            }
            else{
                SIZE_COLUMN = Integer.parseInt(strTemp);
            }
        }catch(java.lang.NumberFormatException ex){
            Logger.getLogger(ControlarTablaDistribucion.class.getName()).log(Level.SEVERE, null, ex);            
            SIZE_COLUMN = 7;
        }
    }

    /**
     * Constructor que inicializa los Controles Swing que se Controlarán en el Proceso
     * de Configuración de la Distribución del <code>ComponenteDistribuidor</code>.
     * @param objTabla <code>JTable</code> que Almacenará la Distribución Actual.
     * @param objList <code>JList</code> que Almacenará la Colección de <code>Componente</code>s Incluidos.
     * @param objComponenteDistribuidorParent <code>ComponenteDistribuidor</code> que se Configurará.
     */
    public ControlarTablaDistribucion(JTable objTabla, JList objList, ComponenteDistribuidor objComponenteDistribuidorParent) {
        try{
            this.objTable = objTabla;
            this.objList = objList;
            this.objComponenteDistribuidorParent = objComponenteDistribuidorParent;       
            objTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);                
            this.aplicarDnD();        
        }catch(java.lang.Exception ex){}       
    }

    /**
     * Devuelve la Referencia del <code>ComponenteDistribuidor</code> de la
     * distribución Actual.
     * @return Referencia del <code>ComponenteDistribuidor</code> de la
     * distribución Actual.
     */
    public ComponenteDistribuidor getObjComponenteDistribuidorParent() {
        return objComponenteDistribuidorParent;
    }
    
    
    
    /**
     * Actualiza la Distribución Actual del <code>Componente</code> por parte del 
     * Usuario partiendo de la Configuración de los Controles Swing que Representan 
     * el GridBagConstraints Actual en la Selección.
     * @param objComponente <code>Componente</code> seleccionado en la Distribución Actual.
     * @param update Determina la Orientación del nuevo valor de la Distribución Actual.
     * @param value Valor de la Orientación de la Distribución Actual.
     * @return boolean que determina el resultado del Proceso.
     */
    public boolean actualizarDistribucionPropiedad(Componente objComponente, int update, int value){        
        
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            //Se Obitene el GridBagConstraints Activo
            GridBagConstraints objGridBagConstraints = objComponente.getGridBagConstraintsActiva();
            //Se extraen las Coordenadas iniciales
            int x = objGridBagConstraints.gridx;
            int y = objGridBagConstraints.gridy;
            boolean ubicar = true;
            //Se evaluan los limites
            if(ControlarTablaDistribucion.UPDATE_SIZE_X == update && this.evalLimitX(x, value)){
                ubicar = this.adicionarColumnas((x + value) - objDefaultTableModel.getColumnCount());
            }
            else if(ControlarTablaDistribucion.UPDATE_SIZE_Y == update && this.evalLimitY(y, value)){
                ubicar = this.adicionarFilas((y + value) - objDefaultTableModel.getRowCount());
            }
            
            //si todo esta correcto se preparael proceso de inclusion en la tabla
            if(ubicar){
                try{
                    int columna = (update == ControlarTablaDistribucion.UPDATE_SIZE_X) ? value : objGridBagConstraints.gridwidth;
                    int fila = (update == ControlarTablaDistribucion.UPDATE_SIZE_Y) ? value : objGridBagConstraints.gridheight;
                    if(ControlarTablaDistribucion.UPDATE_SIZE_X == update){
                    objGridBagConstraints.gridwidth = value;                    
                    }
                    else if(ControlarTablaDistribucion.UPDATE_SIZE_Y == update){
                        objGridBagConstraints.gridheight = value;                    
                    }
                    //objComponente.setGridBagConstraintsActiva(objGridBagConstraints);
                    String strValue = objComponente.getName();                                        
                    ubicarEnDistribucion(strValue, x, y, fila, columna, objComponente);
                }catch(java.lang.Exception ex){
                    return false;
                }                            
            }
            return ubicar;
        }catch(java.lang.Exception ex){            
        }
        return false;
    }
    
    /**
     * Ingresa ó Actualiza el <code>Componente</code> en la Distribución Actual
     * por medio del Proceso de Arrastrar y Soltar (DnD).
     * @param objComponente <code>Componente</code> Actual que se ingresará o Moverá
     * en la Distribución Actual.
     * @return boolean que determina el resultado del Proceso.
     */
    public boolean actualizarDistribucionDnD(Componente objComponente){
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            //Se Obitene el GridBagConstraints Activo
            GridBagConstraints objGridBagConstraints = objComponente.getGridBagConstraintsActiva();
            //Se extraen las Coordenadas iniciales
            int x = objGridBagConstraints.gridx;
            int y = objGridBagConstraints.gridy;
            int height = objGridBagConstraints.gridheight;
            int width = objGridBagConstraints.gridwidth;
            
            boolean ubicar = true;
            //Se evaluan los limites
            if(this.evalLimitX(x, width)){
                ubicar = this.adicionarColumnas(((x + width) - objDefaultTableModel.getColumnCount()));
            }
            
            if(this.evalLimitY(y, height)){
                ubicar = this.adicionarFilas((y + height) - objDefaultTableModel.getRowCount());
            }
            //si todo esta correcto se preparael proceso de inclusion en la tabla
            if(ubicar){
                return this.ubicarComponentePorDnD(objComponente);
            }
            return ubicar;
        }catch(java.lang.Exception ex){            
        }
        return false;
    }
    
    /**
     * Elimina el <code>Componente</code> que es representado por el Parámetro en
     * la Distribución Actual.     
     * @param strValue Nombre del <code>Componente</code> actual que se quitará
     * de la Distribución actual.
     * <p>Nota: Este Proceso no Elimina el <code>Componente</code> de la 
     * Distribución Actual.</p>
     */
    public void limpiarComponente(String strValue){
        if(strValue != null){
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < objDefaultTableModel.getRowCount(); i++){
                for(int j = 0; j < objDefaultTableModel.getColumnCount(); j++){
                    String strTemp = (String) objDefaultTableModel.getValueAt(i, j);
                    if(strTemp != null){
                        if(strTemp.equals(strValue)){
                            objDefaultTableModel.setValueAt("", i, j);
                        }
                    }
                }
            }
            updateModel(objDefaultTableModel);
        }else{
            UtilRender.printlnConsole("StrValue Nulo.", UtilRender.ACTION_NORMAL, false);            
        }
        
    }
    
    /**
     * Evalua el Limite Horizontal en la Distribución Actual.
     * @param x Posicion Inicial del Nombre del <code>Componente</code> por el
     * cuál se evaluará el Limite Horizontal.
     * @param value Nuevo Posible Valor para el Tamaño Horizontal del <code>Componente</code>
     * por el cuál se evaluará el Limite Horizontal.
     * @return boolean que determina si el Nuevo Tamaño se puede distribuir en la
     * Distribución Actual.
     */
    public boolean evalLimitX(int x, int value){
        if(this.objTable == null)return false;
        DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
        return (x + value) > objDefaultTableModel.getColumnCount();
    }
    
    /**
     * Evalua el Limite Vertical en la Distribución Actual.
     * @param y Posicion Inicial del Nombre del <code>Componente</code> por el
     * cuál se evaluará el Limite Vertical.
     * @param value Nuevo Posible Valor para el Tamaño Vertical del <code>Componente</code>
     * por el cuál se evaluará el Limite Vertical.
     * @return boolean que determina si el Nuevo Tamaño se puede distribuir en la
     * Distribución Actual.
     */
    public boolean evalLimitY(int y, int value){
        if(this.objTable == null) return false;
        DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
        return (y + value) > objDefaultTableModel.getRowCount();
    }
    
    /**
     * Agrega una Cantidad determinada de Columnas a la Distribución Actual.
     * @param columnas Cantidad determinada de Columnas que se agregarán a la
     * Distribución Actual.
     * @return boolean que determina el resultado del Proceso.
     */
    public boolean adicionarColumnas(int columnas){        
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < columnas; i++){
                TableColumn objColumna = new TableColumn();                                                
                objDefaultTableModel.addColumn(objColumna);
            }            
            //Se inicializa las Columna a Cadena Vacia
            for(int i = 0; i < objDefaultTableModel.getRowCount(); i++){
                for(int j = objDefaultTableModel.getColumnCount() - 1; j > columnas; j--){                
                    objDefaultTableModel.setValueAt("", i, j);
                }
            }            
            updateModel(objDefaultTableModel);
            return true;
        }catch(java.lang.NullPointerException ex){            
        }
        return false;
    }
    
    /**
     * Agrega una Cantidad determinada de Filas a la Distribución Actual.
     * @param filas Cantidad determinada de Filas que se agregarán a la
     * Distribución Actual.
     * @return boolean que determina el resultado del Proceso.
     */
    public boolean adicionarFilas(int filas){
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < filas; i++){            
                int columnas = objDefaultTableModel.getColumnCount();
                Object nuevaFila[] = new Object[columnas];
                objDefaultTableModel.addRow(nuevaFila);
            }            
            updateModel(objDefaultTableModel);
            return true;
        }catch(java.lang.NullPointerException ex){            
        }
        return false;
    }

    /**
     * Devuelve el <code>TableModel</code> que representa la Distribución Actual.
     * @return <code>TableModel</code> que representa la Distribución Actual.
     */
    public TableModel getObjTableModel() {
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            return objDefaultTableModel;
        }catch(java.lang.NullPointerException ex){}
        return null;
    }
    
    /**
     * Devuelve la Posición Inicial Horizontal del <code>Componente</code> actual
     * distribuido representado por el <code>String</code>.
     * @param strValue <code>String</code> que representa el <code>Componente</code>.
     * @return Posición Inicial del <code>Componente</code> ó (-1) en caso de
     * no estar presente en la Distribución Actual.
     */
    public int devolverPosicionInicialHorizontal(String strValue){
        try{
            if(this.objTable == null)return  0;
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < objDefaultTableModel.getRowCount(); i++){
                for(int j = 0; j < objDefaultTableModel.getColumnCount(); j++){
                    String strTempValue = (String) objDefaultTableModel.getValueAt(i, j);
                    if(strTempValue != null){
                        if(!strTempValue.isEmpty()){
                            if(strTempValue.equals(strValue)){
                                return j;
                            }
                        }
                    }
                }        
            }
            return -1;
        }catch(java.lang.Exception ex){}
        return 0;
    }
    
    /**
     * Devuelve la Posición Inicial Vertical del <code>Componente</code> actual
     * distribuido representado por el <code>String</code>.
     * @param strValue <code>String</code> que representa el <code>Componente</code>.
     * @return Posición Inicial del <code>Componente</code> ó (-1) en caso de
     * no estar presente en la Distribución Actual.
     */
    public int devolverPosicionInicialVertical(String strValue){
        try{
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < objDefaultTableModel.getRowCount(); i++){
                for(int j = 0; j < objDefaultTableModel.getColumnCount(); j++){
                    String strTempValue = (String) objDefaultTableModel.getValueAt(i, j);
                    if(strTempValue != null){
                        if(!strTempValue.isEmpty()){
                            if(strTempValue.equals(strValue)){
                                return i;
                            }
                        }
                    }
                }        
            }
            return -1;
        }catch(java.lang.Exception ex){}
        return 0;
    }
    
    /**
     * Devuelve la Longitud Horizontal del <code>Componente</code> actual
     * distribuido representado por el <code>String</code>.
     * @param strValue <code>String</code> que representa el <code>Componente</code>.
     * @return Posición Inicial del <code>Componente</code> ó (0) en caso de
     * no estar presente en la Distribución Actual.
     */
    public int devolverLongitudHorizontal(String strValue){
        try{
            if(this.objTable == null)return 0;
            int size = 0;
            int fila = this.devolverPosicionInicialHorizontal(strValue);
            int columna = this.devolverLongitudVertical(strValue);
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            if(fila > -1 && columna > -1){
                for(int j = columna; j < objDefaultTableModel.getColumnCount(); j++){
                    String strTempValue = (String) objDefaultTableModel.getValueAt(fila, j);
                    if(strTempValue != null){
                        if(!strTempValue.isEmpty()){
                            if(strTempValue.equals(strValue)){
                                size++;
                                continue;
                            }
                        }
                    }
                    break;
                }
            }
            return size;
        }catch(java.lang.Exception ex){}
        return 0;
    }
    
    /**
     * Devuelve la Longitud Vertical del <code>Componente</code> actual
     * distribuido representado por el <code>String</code>.
     * @param strValue <code>String</code> que representa el <code>Componente</code>.
     * @return Posición Inicial del <code>Componente</code> ó (0) en caso de
     * no estar presente en la Distribución Actual.
     */
    public int devolverLongitudVertical(String strValue){
        try{
            if(this.objTable == null)return 0;
            int size = 0;
            int fila = this.devolverPosicionInicialHorizontal(strValue);
            int columna = this.devolverLongitudVertical(strValue);
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            if(fila > -1 && columna > -1){
                for(int i = fila; i < objDefaultTableModel.getRowCount(); i++){
                    String strTempValue = (String) objDefaultTableModel.getValueAt(i, columna);
                    if(strTempValue != null){
                        if(!strTempValue.isEmpty()){
                            if(strTempValue.equals(strValue)){
                                size++;
                                continue;
                            }
                        }
                    }
                    break;
                }
            }
            return size;
        }catch(java.lang.NullPointerException ex){}
        return 0;
    }
    
    /**
     * Actualiza el <code>TableModel</code> que representa la Distribución Actual.     * 
     */
    public void updateModel(){
       try{
            objTable.setModel(this.objComponenteDistribuidorParent.getObjModeloTablaDistribucion());      
            cargarTitleColumn();
            cargarRenderColumn();
            //objTable.setDefaultRenderer(String.class, new ColumnCellRendererDistribucionComponente());
            objTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);                        
            objTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            ajustarAnchoColumnas();
            objTable.doLayout();
            aplicarDnD();
        
        }catch(java.lang.NullPointerException ex){}
       
    }    
    
    private void updateModel(TableModel objTableModel){
        objTable.setModel(objTableModel); 
        cargarTitleColumn();
        cargarRenderColumn();
        objTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);                        
        objTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        ajustarAnchoColumnas();
        objTable.doLayout();
        aplicarDnD();
    }
    
    private void ajustarAnchoColumnas(){
        int max = maxSpaceCols();
        for(int i = 0; i < objTable.getColumnCount(); i++) {
            objTable.getColumnModel().getColumn(i).setPreferredWidth(max);
        }
    }
    
    private int maxSpaceCols(){
        int max = 20;         
        for(Componente objComponente : this.objComponenteDistribuidorParent.getComponentesDistribuidos()){
            if(max < objComponente.getName().length() * SIZE_COLUMN){
                max = objComponente.getName().length() * SIZE_COLUMN;
            }
        }
        return max;
    }
    
    private void cargarRenderColumn(){
        for(int i = 0; i < objTable.getColumnCount(); i++){
            objTable.getColumnModel().getColumn(i).setCellRenderer(new MyTableCellRendererDistribucionComponentes(this.objComponenteDistribuidorParent));
        }
    }
    
    private void cargarTitleColumn(){
        for(int i = 0; i < objTable.getColumnCount(); i++){
            objTable.getColumnModel().getColumn(i).setHeaderValue("Posición " + i);
        }
    }
    
    private void aplicarDnD(){        
        objTable.setDropMode(DropMode.USE_SELECTION);        
        objTable.setDragEnabled(true);                
        MyTableCellTransferHandler objMyTableRowTransferHandler = new MyTableCellTransferHandler(this);        
        objTable.setTransferHandler(objMyTableRowTransferHandler);                        
    }
    
    /**
     * Inicia el Proceso de Ubicación del <code>Componente</code> representado 
     * por el <code>String</code> en la Distribución Actual Iniciando en las 
     * Coordenadas <code>(x,y)</code> por medio del Proceso de Arrastrar y Soltar (DnD).
     * @param strValue <code>String</code> que representa el <code>Componente</code>.
     * @param x Posicion Inicial Horizontal.
     * @param y Posición Inicial Vertical.
     * @return boolean que determinae el resultado del Proceso.
     */
    public boolean ubicarPorDnD(String strValue, int x, int y){
        try{
            if(this.objComponenteDistribuidorParent == null)return false;
            Componente objComponente = this.objComponenteDistribuidorParent.buscarComponente(strValue);
            GridBagConstraints objGridBagConstraints = objComponente.getGridBagConstraintsActiva();
            objGridBagConstraints.gridx = x;
            objGridBagConstraints.gridy = y;
            objComponente.setGridBagConstraintsActiva(objGridBagConstraints);
            return this.actualizarDistribucionDnD(objComponente);
        }catch(java.lang.NullPointerException ex){
            System.out.println("No existe Componente: " + strValue + " en la Colección " + objComponenteDistribuidorParent.getName());
        }
        return false;
    }
    
    
    private boolean ubicarComponentePorDnD(Componente objComponente){
        try{
            //Se Obitene el GridBagConstraints Activo
            GridBagConstraints objGridBagConstraints = objComponente.getGridBagConstraintsActiva();
            //Se extraen las Coordenadas iniciales
            int x = objGridBagConstraints.gridx;
            int y = objGridBagConstraints.gridy;            
            int columna = objGridBagConstraints.gridwidth;
            int fila = objGridBagConstraints.gridheight;
            String strValue = objComponente.getName();
            ubicarEnDistribucion(strValue, x, y, fila, columna, objComponente);
            return true;
        }catch(java.lang.Exception ex){
        }
        return false;
    }

    private void ubicarEnDistribucion(String strValue, int x, int y, int fila, int columna, Componente objComponente) {
        //if(this.ubicar(strValue, x, y, fila, columna)){            
            if(objComponenteDistribuidorParent.agregarComponenteDistribuido(objComponente)){                                            
                PlantillaCombo objNewPlantillaCombo = (PlantillaCombo) objComponenteDistribuidorParent.getObjPlantillaComboDistribucionGrupalSeleccionada().clone();                
                PlantillaComponente objNewPlantillaComponente = GeneradorComponentes.crearPlantillaComponente(objComponente);
                
                objComponente.setObjPlantillaComboDistribucionIndividualSeleccionada(objNewPlantillaCombo);
                objComponente.agregarDistribucionIndividual(objNewPlantillaCombo, objNewPlantillaComponente);
                if(this.objComponenteDistribuidorParent.ubicarComponentesEnTablaDistribucion()){
                    this.updateModel();
                }
                loadRendererJListInluidos();
            }
        //}
    }
    
    private boolean ubicar(String strValue, int x, int y, int fila, int columna){
        try{
            this.limpiarComponente(strValue);
            DefaultTableModel objDefaultTableModel = (DefaultTableModel) this.objTable.getModel();
            for(int i = 0; i < fila; i++){//coordenadas y
                for(int j = 0; j < columna; j++){//coordenadas x
                    objDefaultTableModel.setValueAt(strValue, y + i, x + j);
                }
            }
            updateModel(objDefaultTableModel);
            return true;
        }catch(java.lang.Exception ex){
        }
        return false;        
    }
    
    /**
     * Carga la Lista de <code>Componente</code>s Incluidos en la Distribución Actual.
     */
    public void loadRendererJListInluidos() {
        try{
            JList objLista = new JList(this.objComponenteDistribuidorParent.getComponentesDistribuidos().toArray());
            this.objList.setModel(objLista.getModel());
            this.objList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

            MyListRendererJLabel objRender = new MyListRendererJLabel(0);
            this.objList.setCellRenderer(objRender);        
        }catch(java.lang.NullPointerException ex){}
        
    }
    
    
}
